<?php
/***************************************************************************
 * 
 * Copyright (c) 2010 , Inc. All Rights Reserved
 * $Id$:DefaultURLDispatch.php,2010/05/07 16:27:19 
 * 
 **************************************************************************/
 
 
 
/**
 * @file DefaultURLDispatch.php
 * @author huqingping
 * @date 2010/05/07 16:27:19
 * @version 1.0 
 * @brief 
 *  
 **/

require_once FR_ROOT.'core/PageController.php';

class DefaultURLDispatch 
{
	private $app = null;
	//不允许用户直接访问的url
	static $protected = array('index','_before','_after');

	function __construct(WebApp $app) 
	{
		$this->app = $app;
	}

	function dispatch($url)
	{
		$args = array();
		$pathseg = array();
		//URL全部转换为小写，变得地址栏里大小写URL表现不一致
		$url = explode('/',trim($url,'/'));
		if ($this->app->request->method == 'GET') {
			//GET请求才有这种灵活策略
			//其他请求严格一点，安全限制要求
			foreach($url as $seg) {
				if (preg_match('/^([a-z0-9]{24,24}|[0-9]+)$/i',$seg)) {
					$args[] = $seg;
				} else {
					$pathseg[] = $seg;
				}
			}
		} else {
			$pathseg = $url;
		}
		$count = count($pathseg);
		if ($count > 0 && in_array($pathseg[$count-1],self::$protected)) {
			//不允许直接访问的页面
			throw new Exception('hapn.u_notfound');
		}
		$fullpath = rtrim(PAGE_ROOT.implode('/',$pathseg),'/');
		$userfunc = '';
		if (is_readable($fullpath.'/ActionController.php')) {
			$path = $fullpath.'/ActionController.php';
			$func = 'index';
			if (empty($args)) $args[] = '';
		} elseif (is_readable($fullpath.'/index/ActionController.php')) {
			$path = $fullpath.'/index/ActionController.php';
			$func = 'index';
			if (empty($args)) $args[] = '';
		} else {
			if ($count > 0) {
				//至少有一级
				$rootseg = array_slice($pathseg, 0, $count-1);
				$path = rtrim(PAGE_ROOT.implode('/',$rootseg),'/').'/ActionController.php'; 
				$func = $pathseg[$count-1];
				$userfunc = $func;
			} else {
				$path = rtrim(PAGE_ROOT,'/').'/ActionController.php';
				$func = 'index';
			}
		}	

		if (strncmp($func,'_',1) === 0) {
			//意味这个url原来设计为提交数据而用
			if ($this->app->request->method === 'GET') {
				//如果请求为GET则出错
				throw new Exception('hapn.u_notfound');
			}
		}
		Logger::debug("hit ActionController %s:%s",$path,$func);
		if (is_readable($path)) {
			require_once $path;
		}
		if (class_exists('ActionController')) {
			$controller = new ActionController();
			//把当前处理请求和相应的对象设置好
			$controller->request = $this->app->request;
			$controller->response = $this->app->response;
			$controller->encoding = $this->app->encoding;
			$controller->debug = $this->app->debug;
			$controller->appId = $this->app->appId;
			//如果是实际能调用的函数则判断下是否有白名单配置
			//如果设置了白名单，但是不在白名单中，则出错
			if (!empty($controller->pages) && is_array($controller->pages)) {
				//如果controller里设置了页面白名单
				if (!in_array($userfunc,$controller->pages)) {
					throw new Exception("hapn.u_notfound protected page $userfunc");
				}
			}

			if ($func != 'index') {
				//index函数允许有一个空串参数
				$args = array_diff($args,array(''));
			}
			try
			{
				$this->_callmethod($controller,'_before',$args);
				if (!$this->_callmethod($controller,$func,$args)) {
					if ($func != 'index') {
						if ($args) {
							//如果页面不是index页面，并且制定了数字参数，则函数本身不能再作为参数了。
							throw new Exception('hapn.u_notfound');
						}
						$args[] = $func;
						if (!$this->_callmethod($controller,'index',$args)) {
							throw new Exception('hapn.u_notfound');
						}
					}
				}
			}catch(Exception $ex) {
				$this->_callmethod($controller,'_after',$args);
				throw $ex;
			}
			$this->_callmethod($controller,'_after',$args);
		} else {
			throw new Exception('hapn.u_notfound');
		}
	}
	private function _callmethod($controller,$method,$args=array())
	{
		if (is_callable(array($controller,$method))) {
			$reflection = new ReflectionMethod($controller,$method);
			$argnum = $reflection->getNumberOfParameters();
			if ($argnum > count($args)) {
				throw new Exception("hapn.u_notfound args not match");
			}
			//公共方法才允许被调用
			$reflection->invokeArgs($controller,$args);
			//Logger::debug('url dispatch call: %s',$method);
			return true;
		}
		return false;
	}
}





/* vim: set ts=4 sw=4 sts=4 tw=100 noet: */
